#ifdef __cplusplus
extern "C" {
#endif
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#ifdef __cplusplus
}
#endif

#include "astar/core/AStarNode.h"
#include "astar/Grid.h"
#include "astar/core/Path.h"
#include <vector>
#include "astar/PathFinder.h"
#include "PathFinder_luabinding.h"
#include <iostream>
using namespace std;


static void * checkuserdata(lua_State *L, int index) {
		void * ud = lua_touserdata(L,index);
		if (ud == NULL) {
			luaL_error(L, "userdata %d is nil",index);
		}
		return ud;
}

static int pathfinder_luabinding_constructor(lua_State* L)
{
	//PathFinder** self = (PathFinder**)lua_newuserdata(L,sizeof(PathFinder*));
	PathFinder *self = new PathFinder();
	//luaL_getmetatable(L, "luaL_PathFinder");
	//lua_setmetatable(L, -2);
	lua_pushlightuserdata(L,self);
	return 1;
}

static int pathfinder_luabinding_init(lua_State* L)
{
	PathFinder* self = (PathFinder *)checkuserdata(L, 1);
	FindersMode fm = ((FindersMode) (int)  lua_tonumber(L,2));
	SearchMode sm = ((SearchMode) (int)  lua_tonumber(L,3));
	HeuristicMode hm = ((HeuristicMode) (int)  lua_tonumber(L,4));
	bool tunnel = ((bool)  lua_toboolean(L,5));

	bool ret = (bool) self->init(fm,sm,hm,tunnel);
	lua_pushboolean(L,ret);
	return 1;
}

static int pathfinder_luabinding_setMapData(lua_State* L)
{
	PathFinder* self = (PathFinder *)checkuserdata(L, 1);
	const char *mapData = lua_tostring(L, 2);
	int blockVal = ((int)  lua_tonumber(L,3));
	int width = ((int)  lua_tonumber(L,4));
	int height = ((int)  lua_tonumber(L,5));
	// int8** tb=NULL;
	// tb = new int8*[height];

	// for (int i=0;i<height;++i)
	// {
	// 	tb[i] = new int8[width];
	// 	memset(tb[i],0,width*sizeof(int8));
	// }

	// for (unsigned int i=0; i < height; ++i)
	// {
	// 	for (unsigned int j=0;j < width; ++j)
	// 	{
	// 		uint8 val = 0;
	// 		uint32 index = i * width + j;
	// 		uint8 byte = (uint8)mapData[index / 8];
	// 		uint8 bitIndex = (index % 8);
	// 		val = ((byte >> bitIndex) % 2);
	// 		tb[i][j] = val;
	// 	}
	// }

	self->setMapData(mapData,blockVal,width,height);
	return 0;
}

static int pathfinder_luabinding_modifyMapData(lua_State* L)
{
	PathFinder* finder = (PathFinder *)checkuserdata(L, 1);
	Grid* grid = finder->getGrid();
	lua_pushnil(L);
	while(lua_next(L, 2) != 0)
	{
		int index = lua_tointeger(L, -2);
		int value = lua_tointeger(L, -1);
		grid->setNodeAt(index, value);
		lua_pop(L, 1);
	}

	return 0;
}

static int pathfinder_luabinding_getPathInTable(lua_State* L)
{

	PathFinder* finder = (PathFinder *)checkuserdata(L, 1);
	int startX = ((int)  lua_tonumber(L,2));
	int startY = ((int)  lua_tonumber(L,3));
	int endX = ((int)  lua_tonumber(L,4));
	int endY = ((int)  lua_tonumber(L,5));
	bool filter =((bool) lua_toboolean(L,6));

	if (!finder) return 0;

	Path* p = finder->getPath(startX,startY,endX,endY);

	if(!p) return 0;

	if (filter)
		p->filter();

	lua_newtable(L);

	for (size_t i = 0; i < p->_nodes.size(); ++i)
	{
		AStarNode* n = p->_nodes[i];
		lua_newtable(L);

		lua_pushstring(L,"x");
		lua_pushinteger(L,n->x);
		lua_rawset(L,-3);

		lua_pushstring(L,"y");
		lua_pushinteger(L,n->y);
		lua_rawset(L,-3);

		lua_rawseti(L,-2,i+1);
	}
	return 1;
}

static int _clear_gcobj(lua_State *L)
{
	struct PathFinderGC * obj = (struct PathFinderGC *)lua_touserdata(L,1);
	delete obj->_gcObj;
	obj->_gcObj = NULL;
	return 0;
}

static int pathfinder_luabinding_GC(lua_State * l)
{
	struct PathFinderGC * gcObj = (struct PathFinderGC *)lua_newuserdata(l,sizeof(PathFinderGC));
	PathFinder* finder = (PathFinder *)checkuserdata(l, 1);
	gcObj->_gcObj = finder;

	lua_createtable(l,0,1);
	lua_pushcfunction(l, _clear_gcobj);
	lua_setfield(l,-2,"__gc");
	lua_setmetatable(l,-2);
	return 1;
}

int luaopen_pathfinder_luabinding(lua_State * l)
{
	luaL_Reg spathfinderRegs[] =
	{
		{ "new", pathfinder_luabinding_constructor },
		{ "_gc", pathfinder_luabinding_GC },
		{ "init",pathfinder_luabinding_init},
		{ "setMapData",pathfinder_luabinding_setMapData},
		{ "modifyMapData",pathfinder_luabinding_modifyMapData},
		{ "getPath",pathfinder_luabinding_getPathInTable},
		{ NULL, NULL }
	};

	luaL_register(l, "pathfinder.c", spathfinderRegs);

	return 1;
}

